#ifndef __W_SESSION_H__
#define __W_SESSION_H__
#include "online.hpp"


typedef enum{ UNLOGIN, LOGIN } ss_statu;    //session 状态
class session
{
public:
    session(uint64_t ssid):_ssid(ssid) { DLOG("SESSION %p 被创建！！", this); }
    ~session(){ DLOG("SESSION %p 被销毁！！", this); }
    // 
    uint64_t get_ssid() { return _ssid; }      //获取session id
    void set_user(uint64_t uid) { _uid = uid; } //设置用户id
    void set_statu(ss_statu statu) { _statu = statu; }  //设置session状态
    uint64_t get_user() { return _uid; }    //获取用户id
    bool is_login() { return (_statu == LOGIN); }   //判断session是否为登录状态
    void set_time(const wsserver_t::timer_ptr& tp) { _tp = tp; }   //设置定时器对象
    wsserver_t::timer_ptr& get_time() { return _tp; }      //返回定时器对象
private:
    uint64_t _ssid; //标识符
    uint64_t _uid;  //session对应的用户id
    ss_statu _statu;    //用户状态
    wsserver_t::timer_ptr _tp;   //session关联的定时器
};

#define SESSION_TIME 30000  //session定时销毁的时间
#define SESSION_FOREVER -1  //设置session为永久存在

using session_ptr = shared_ptr<session>;
class session_manager
{
public:
    session_manager(wsserver_t* svr):_next_ssid(1),_server(svr)
    {
        DLOG("session管理器初始化完毕！");
    }
    ~session_manager(){ DLOG("session管理器即将销毁！"); }
    // 创建session
    session_ptr create_session(uint64_t uid, ss_statu statu)
    {
        unique_lock<mutex> lock(_mutex);
        session_ptr ssp(new session(_next_ssid));
        _session.insert(make_pair(_next_ssid, ssp));
        ssp->set_statu(statu);
        ssp->set_user(uid);
        _next_ssid++;   //ssid自增
        return ssp;
    }
    // 通过ssid获取会话信息
    session_ptr get_session_by_ssid(uint64_t ssid)
    {
        unique_lock<mutex> lock(_mutex);
        auto it = _session.find(ssid);
        if(it == _session.end())
        { return session_ptr(); }

        return it->second;
    }
    // 删除会话信息
    void remove_session(uint64_t ssid)
    {
        unique_lock<mutex> lock(_mutex);
        _session.erase(ssid);
    }
    // 添加会话信息
    void append_session(session_ptr& ssp)
    {
        unique_lock<mutex> lock(_mutex);
        _session.insert(make_pair(ssp->get_ssid(), ssp));
    }
    // 设置会话的生命周期
    void set_session_expire_time(uint64_t ssid, int ms)
    {
        // 依赖于wsserver_tpp的定时器来完成session生命周期的管理
        // 登陆之后，创建session，session在指定时间无通信后被删除
        // 进入游戏大厅/游戏房间，session就应该永久存在
        // 退出游戏大厅/游戏房间，session重新被设置为临时的，长时间无通信后被删除
        session_ptr ssp = get_session_by_ssid(ssid);
        if(ssp.get() == nullptr)
        { return; }

        // 1. 在session永久存在的情况下，被设置为永久
        wsserver_t::timer_ptr tp = ssp->get_time();
        if(tp.get() == nullptr && ms == SESSION_FOREVER)
        { return; }
        // 2. 在session永久存在的情况下，设置一个定时任务，指定时间后被删除
        else if(tp.get() == nullptr && ms != SESSION_FOREVER)
        {
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms, 
                bind(&session_manager::remove_session, this, ssid));
            ssp->set_time(tmp_tp);
        }
        // 3. 在session被设置了定时删除的情况下，将session设置为永久存在
        else if(tp.get() != nullptr && ms == SESSION_FOREVER)
        {
            // 删除定时任务 -> stready_timer 删除任务会导致任务被直接执行
            tp->cancel(); //但是这个取消定时任务并不是立即被执行的
            // 因此要给session管理器中重新添加一个session信息
            // 且添加时需要定时器，而不是立即添加，因为可能会出现，刚添加就正好被取消的情况
            ssp->set_time(wsserver_t::timer_ptr());
            _server->set_timer(0,bind(&session_manager:: append_session,this,ssp));
        }
        // 4. 在session被设置了定时删除的情况下，将session 重置删除时间
        else if(tp.get() != nullptr && ms != SESSION_FOREVER)
        {
            // 先取消，把信息添加之后，在重新设置
            tp->cancel();
            _server->set_timer(0,bind(&session_manager::append_session,this,ssp));
            ssp->set_time(wsserver_t::timer_ptr());
            // 重新设置定时器
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms, 
                bind(&session_manager::remove_session, this, ssid));
            ssp->set_time(tmp_tp);
        }
    }
private:
    uint64_t _next_ssid;
    mutex _mutex;
    unordered_map<uint64_t, session_ptr> _session;  //ssid和session信息的关联
    wsserver_t* _server; 
};

#endif